package com.wbvideo.resample;

import android.annotation.TargetApi;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.media.MediaFormat;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.Log;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


public class MusicDataManager {

    public static final String TAG = "MusicDataManager";
    private static MusicDataManager mInstance;

    protected String mAudioPath;

    //硬解
    private HardMusicDecoder mHardMusicDecoder;

    private AudioTrack mAudioTrack;
    private boolean mPlayingRunning;
    private float mVolume = 0.5f;

    private int mSampleRate;
    private int defaultBufferSize = 4096;

    //自定义线程池配置，不需要核心线程，线程keepAlive时间30s
    private ExecutorService mExecutorPool =
            new ThreadPoolExecutor(2, Integer.MAX_VALUE, 30L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());

    public MusicDataManager() {
    }

    public static MusicDataManager getInstance() {
        if (mInstance == null) {
            synchronized (MusicDataManager.class) {
                if (mInstance == null)
                    mInstance = new MusicDataManager();
            }
        }
        return mInstance;
    }

    public String getAudioPath() {
        return mAudioPath;
    }

    public void setDefaultBufferSize(int defaultBufferSize) {
        this.defaultBufferSize = defaultBufferSize;
    }

    public void startPlayMusic(String audioPath, int sampleRate, float volume) throws Exception {
        this.mAudioPath = audioPath;
        this.mSampleRate = sampleRate;
        this.mVolume = volume;

        File file = new File(audioPath);
        if (!file.exists()) {
            throw new IOException("audioPath not exists");
        }

        stopPlayMusic();

        mPlayingRunning = true;
        mExecutorPool =
                new ThreadPoolExecutor(2, Integer.MAX_VALUE, 30L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            mHardMusicDecoder = new HardMusicDecoder(audioPath, mSampleRate);
            mHardMusicDecoder.startPcmExtractor();

            HardAudioRunnable hardAudioRunnable = new HardAudioRunnable();
            hardAudioRunnable.setRunning(true);
            mExecutorPool.execute(hardAudioRunnable);
        }
    }

    public void stopPlayMusic() {
        mPlayingRunning = false;
        if (null != mExecutorPool) {
            mExecutorPool.shutdown();
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            if (null != mHardMusicDecoder) {
                mHardMusicDecoder.release();
            }
        }
    }

    FileOutputStream mFileOutputStream;

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    private class HardAudioRunnable implements Runnable {
        boolean foundFrame;
        byte[] audioByte = null;

        private ByteBuffer mInByteBuffer;
        private int inBufferSize = 0;
        private int mBufferAllocUnitSize = 10000;

        public HardAudioRunnable() {
            mPlayingRunning = true;
            MediaFormat mediaFormat = mHardMusicDecoder.getMediaFormat();

            int sampleRate = mSampleRate;
            int androidChannel = 0;
            int encoding = AudioFormat.ENCODING_PCM_16BIT;

            int nChannel = mediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
            if (nChannel == 1) {
                androidChannel = AudioFormat.CHANNEL_OUT_MONO;
            } else if (nChannel == 2) {
                androidChannel = AudioFormat.CHANNEL_OUT_STEREO;
            }

            int bufferSize = AudioTrack.getMinBufferSize(sampleRate, androidChannel, encoding);

            mAudioTrack = new AudioTrack(
                    AudioManager.STREAM_MUSIC,
                    sampleRate,
                    androidChannel,
                    encoding,
                    2 * bufferSize,
                    AudioTrack.MODE_STREAM
            );
            if (Build.VERSION.SDK_INT >= 21) {
                mAudioTrack.setVolume(mVolume);
            } else {
                mAudioTrack.setStereoVolume(mVolume, mVolume);
            }
            mAudioTrack.play();

            mInByteBuffer = ByteBuffer.allocate(2 * mBufferAllocUnitSize);
            mInByteBuffer.clear();
            inBufferSize = 0;


            try {
                File outFile = new File("/sdcard/hard_resample.wav");
                if (outFile.exists()) {
                    outFile.delete();
                }
                mFileOutputStream = new FileOutputStream(outFile);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }

        @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
        @Override
        public void run() {
            while (true) {
                if (!mPlayingRunning) {
                    break;
                }
                foundFrame = false;


                final byte[] buf = new byte[defaultBufferSize];

                mHardMusicDecoder.getPCMData(new IDecoderProcessListener() {

                    @Override
                    public void onProcess(byte[] bytes) {
                        audioByte = bytes;

                        if (null != audioByte) {
                            Log.d("JIN", "onProcess....");
                            try {
                                mFileOutputStream.write(audioByte);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }

                            foundFrame = true;
                            int audioPCMLength = audioByte.length;

                            if (audioPCMLength > mBufferAllocUnitSize) {
                                mBufferAllocUnitSize = audioPCMLength;
                                mInByteBuffer = ByteBuffer.allocate(2 * mBufferAllocUnitSize);
                                mInByteBuffer.clear();
                                inBufferSize = 0;
                            }
                            while (inBufferSize >= defaultBufferSize) {
                                nsProcessInner(buf);
                            }
                            mInByteBuffer.put(audioByte);
                            inBufferSize += audioByte.length;
                        }
                    }
                });

                if (!foundFrame) {
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        private void nsProcessInner(byte[] buf) {
            mInByteBuffer.rewind();
            mInByteBuffer.get(buf, 0, buf.length);

            byte[] inBufferLeft = new byte[inBufferSize - mInByteBuffer.position()];
            mInByteBuffer.get(inBufferLeft);
            mInByteBuffer.rewind();
            mInByteBuffer.clear();
            inBufferSize = 0;

            mInByteBuffer.put(inBufferLeft);
            inBufferSize += inBufferLeft.length;

            if (mAudioTrack != null && mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
                mAudioTrack.write(buf, 0, buf.length);
            }
        }

        void setRunning(boolean running) {
            mPlayingRunning = running;
        }
    }
}
